home *** CD-ROM | disk | FTP | other *** search
- // "Snow.c" - Snow on your desktop, THE APPLICATION!
- // Written November, 1994 by Dave Warker
- // mailto: dwarker@acy.digex.net or davew@water.waterw.com
- // Symantec/Think "C".
- //
- // Floats a lovely blanket of snowflakes down the screen.
- //
- // This app also continuously plays sleigh bells while it is
- // the active application. Sound is suppressed if the Sound Manager
- // is not available or if the user has set the volume to zero.
- // A single sleigh bell shake is used. Each cycle uses a slightly
- // different volume and pitch to prevent it from sounding like
- // a demented motor boat.
- //
- // If you create something interesting with this code, please
- // send me copy.
- //
- // 11/95 dww -- updated for PPC, Universal headers and Think 7.
-
- #include <LowMem.h>
- #include <Sound.h>
- #include <Traps.h>
-
-
- /// - constants.
-
- enum
- {
- cursorID = 128, // id for 'CURS' resource
-
- menuBarID = 128, // 'MBAR' resource id
- appleMenuID = 128, // 'apple' menu
- aboutSnow = 1, // "about" box
- fileMenuID = 129, // 'File' menu
- sleighBellsFile = 1, // "Sleigh Bells"
- quitFile = 3, // "Quit"
-
- aboutAlertID = 128, // 'ALRT' id for about box
-
- maxFlakes = 71, // maximum flakes active at one time
- stdFlakeDelay = 5, // longest delay between flake moves
-
- #define iconType 'SICN' /* resource type for icon lsit */
- iconID = 128, // and its resource ID
- iconSize = 16, // width/height of an icon (SICN)
- iconShift = 5, // shift left to convert index to offset
-
- baseVolume = 20, // channel is at least this loud
- deltaVolume = 7, // plus up to this (must be 2^n-1)
-
- deltaRate = 0x2000 // rate varies up or down by this amount (2^n)
- };
-
-
- /// - datatypes.
-
- typedef Byte IconData[iconSize*iconSize/8];
- // Data for an icon.
-
- typedef struct
- // Info for a single flake.
- {
- short offset; // offset into icon list
- #define noFlake (-1)
- Point where; // "center" coords for the flake
- short haxis; // preferred horizontal position
- Boolean new; // TRUE if not previously drawn
- }
- Flake;
-
-
- /// - globals.
-
- Boolean running = true; // set 'false' to terminate
- Boolean inForeground = true; // 'true' while in foreground
- unsigned long nextTicks; // when need to update flakes
- EventRecord event; // current event being processed
-
- Flake flakes[maxFlakes] = {0}; // Current blizzard
- unsigned short nFlakes = 0; // number of active flakes
- Boolean flakesHiding = false; // 'true' when flakes must be redrawn
-
- GrafPort snowPort; // where we do all the drawing
- Handle hIcons = nil; // handle to list of icons
- unsigned short nIcons = 0; // number of icons available
- BitMap icons; // bit map of all snowflakes
- BitMap temp; // used during move of flakes
- IconData tempData; // storage for the bitmap
- short groundV; // flakes below here are off screen
-
- Boolean bgWanted; // set if want background sound
- SndChannelPtr bgChannel = nil; // channel playing background sound
- Handle bgSndHandle = nil; // handle to background sound
- SoundHeader* bgSndPtr = nil; // points to 'bufferCmd' to play sound
- short bgSndSynth; // resource ID of synth for this sound
- long bgSndInit; // initialization for sound channel
- Fixed bgSndBaseRate; // base playback rate
-
-
- /// - interface.
-
- void main(void);
-
- static void MacSetup(void);
- static void FlakeCursor(void);
- static void DoEvent(void);
- static void DoMenuChoice(long);
-
- static void SnowSetup(void);
- static void DanceFlakesDance(void);
- static void AddFlakes(void);
- static void MoveFlakes(void);
- static void GetFlakeRects(Flake*, Rect*, Rect*);
- static short BlowFlake(Flake*);
- static void HideFlakes(void);
- static void ToggleFlakes(void);
-
- static void PrepBgSnd(void);
- static void EndBgSnd(void);
- static void StartBgSnd(void);
- static void StopBgSnd(void);
- pascal void PlayBgSnd(SndChannelPtr, SndCommand*);
-
- #define randu() ((unsigned short) Random())
-
-
- /// - implementation.
-
- void main(void)
- {
- MacSetup();
- SnowSetup();
- PrepBgSnd();
-
- do{
- Boolean gotOne;
- if (inForeground)
- // Chug away as fast as we can.
- { SystemTask(); gotOne = GetNextEvent(everyEvent, &event); }
- else
- // Be nice to Mr. Foreground application.
- // (We know WNE is available cause we got a Suspend event!)
- gotOne = WaitNextEvent(everyEvent, &event, 0x3fffffffL, nil);
-
- if (gotOne)
- DoEvent();
- else if (inForeground)
- DanceFlakesDance();
-
- }while (running);
-
- HideFlakes();
- EndBgSnd();
- }
-
-
- static void MacSetup(void)
- {
- Handle mbar;
-
- // Standard stuff.
- MaxApplZone();
- MoreMasters(); MoreMasters();
- InitGraf(&qd.thePort);
- InitFonts();
- InitWindows();
- InitMenus();
- TEInit();
- InitDialogs(0);
-
- // Menu bar.
- mbar = GetNewMBar(menuBarID);
- if (mbar == nil) ExitToShell();
- SetMenuBar(mbar);
- DisposHandle(mbar);
- AddResMenu(GetMHandle(appleMenuID), 'DRVR');
- DrawMenuBar();
-
- // Cursor.
- FlakeCursor();
- }
-
- static void FlakeCursor(void)
- {
- CursHandle curs = GetCursor(cursorID);
- if (curs != nil)
- {
- HLock((Handle) curs);
- SetCursor(*curs);
- ReleaseResource((Handle) curs);
- }
- }
-
-
- static void DoEvent(void)
- {
- WindowPtr noWindows;
-
- switch (event.what)
- {
- case keyDown:
- if (event.modifiers & cmdKey)
- {
- HideFlakes();
- DoMenuChoice(MenuKey(event.message & charCodeMask));
- }
- break;
-
- case mouseDown:
- HideFlakes();
- switch (FindWindow(event.where, &noWindows))
- {
- case inMenuBar:
- InitCursor();
- DoMenuChoice(MenuSelect(event.where));
- break;
-
- case inSysWindow:
- // should never get this, but...
- SystemClick(&event, noWindows);
- }
- break;
-
- case osEvt:
- switch ((Byte) (event.message >> 24))
- {
- case suspendResumeMessage:
- inForeground = event.message & 1;
- if (inForeground)
- {
- // Defer redraw for a few ticks to allow Finder to redraw its stuff.
- nextTicks = TickCount() + 10;
- StartBgSnd();
- }
- else
- {
- // Hide flakes until we return.
- HideFlakes();
- StopBgSnd();
- }
- }
- }
- }
-
-
- static void DoMenuChoice(long choice)
- {
- HiliteMenu(choice >> 16);
- switch (choice >> 16)
- {
- case appleMenuID:
- switch ((short) choice)
- {
- case aboutSnow:
- Alert(aboutAlertID, nil);
- break;
-
- default:
- {
- Str255 name; name[0] = 0;
- GetItem(GetMHandle(appleMenuID), (short) choice, name);
- OpenDeskAcc(name);
- }
- }
- break;
-
- case fileMenuID:
- switch ((short) choice)
- {
- case sleighBellsFile:
- bgWanted = ! bgWanted;
- SetItemMark(GetMHandle(fileMenuID), sleighBellsFile, bgWanted ? checkMark : 0);
- if (bgWanted)
- PrepBgSnd();
- else
- EndBgSnd();
- break;
-
- case quitFile:
- running = false;
- }
- }
- HiliteMenu(0);
- }
-
-
- /// - flake stuff.
-
- static void SnowSetup(void)
- {
- unsigned long theTime; RgnHandle gray;
-
- // Prime random number generator.
- GetDateTime(&theTime);
- qd.randSeed = theTime;
-
- // Get a handle to the icon list.
- hIcons = Get1Resource(iconType,iconID);
- if (hIcons == nil) ExitToShell();
- nIcons = GetHandleSize(hIcons) / sizeof(IconData);
-
- // Open up our drawing port. It should span all monitors.
- gray = LMGetGrayRgn();
- OpenPort(&snowPort);
- MovePortTo(
- (*gray)->rgnBBox.left,
- (*gray)->rgnBBox.top);
- PortSize(
- (*gray)->rgnBBox.right - (*gray)->rgnBBox.left,
- (*gray)->rgnBBox.bottom - (*gray)->rgnBBox.top);
- ClipRect(&snowPort.portRect);
- RectRgn(snowPort.visRgn, &snowPort.portRect);
- groundV = snowPort.portRect.bottom + iconSize/2;
-
- // Build bit map to access flakes.
- nIcons = GetHandleSize(hIcons) / sizeof(IconData);
- HLock(hIcons);
- icons.baseAddr = StripAddress(*hIcons);
- icons.rowBytes = iconSize/8;
- icons.bounds.top = icons.bounds.left = 0;
- icons.bounds.right = iconSize;
- icons.bounds.bottom = nIcons * iconSize;
-
- // Build temp bitmap used during move.
- temp.baseAddr = StripAddress(tempData);
- temp.rowBytes = iconSize/8;
- temp.bounds.top = temp.bounds.left = 0;
- temp.bounds.right = temp.bounds.bottom = iconSize;
-
- // Init flake list.
- {
- short i;
- for (i = 0; i < maxFlakes; ++i)
- {
- flakes[i].offset = noFlake;
- flakes[i].new = false;
- }
- nFlakes = 0;
- }
-
- // Determine initial sound setting.
- {
- short mark;
- GetItemMark(GetMHandle(fileMenuID), sleighBellsFile, &mark);
- bgWanted = (mark != 0);
- }
- }
-
-
- static void DanceFlakesDance(void)
- {
- if (event.when >= nextTicks)
- {
- // Set next time to diddle the flakes.
- nextTicks = event.when + stdFlakeDelay;
-
- // Redraw flakes if they were hidden.
- if (flakesHiding)
- {
- ToggleFlakes();
- FlakeCursor();
- flakesHiding = false;
- }
-
- // Add a flake every so often.
- AddFlakes();
-
- // Move current flakes.
- MoveFlakes();
- }
- }
-
-
- static void AddFlakes(void)
- {
- if (nFlakes < maxFlakes)
- {
- if ((randu() & 127) > 115)
- {
- // Find open slot.
- Flake* flakep = flakes;
- while (flakep->offset >= 0)
- ++flakep;
-
- // Select random flake (offset = vertical coord of top in BitMap.)
- flakep->offset = (randu() % nIcons) * iconSize;
-
- // Select random position near top of display.
- flakep->haxis =
- flakep->where.h =
- snowPort.portRect.left + randu() %
- (snowPort.portRect.right - snowPort.portRect.left);
- flakep->where.v = snowPort.portRect.top + (randu() & 7) - iconSize;
- flakep->new = true;
-
- ++nFlakes;
- }
- }
- }
-
-
- static void MoveFlakes(void)
- {
- Flake* flakep = flakes;
- Rect rSicn, rFlake, rBlow;
- short count, delta;
- Point newWhere;
-
- for (count = maxFlakes; count != 0; --count, ++flakep)
- {
- if (flakep->offset >= 0)
- {
- GetFlakeRects(flakep, &rSicn, &rFlake);
- if (flakep->new)
- // Drawing this for the first time.
- flakep->new = false;
- else
- {
- if (flakep->where.v > groundV)
- // Went off screen, erase it and kill it.
- { flakep->offset = -1; --nFlakes; }
- else
- {
- // Alive and kicking, move it down and jiggle it left/right.
- flakep->where.v += 1;
- rBlow.top = 1;
- rBlow.bottom = iconSize+1;
- rBlow.left = BlowFlake(flakep);
- rBlow.right = rBlow.left + iconSize;
-
- // New flake = old XOR new (assumes flakes are centered within
- // icon bounds and there is at least a one pixel border on all sides!)
- CopyBits(&icons, &temp, &rSicn, &temp.bounds, srcCopy, nil);
- CopyBits(&icons, &temp, &rSicn, &rBlow, srcXor, nil);
- CopyBits(&temp, &snowPort.portBits, &temp.bounds, &rFlake, srcXor, nil);
- continue;
- }
- }
- // Draw new flake or erase dead one.
- CopyBits(&icons, &snowPort.portBits, &rSicn, &rFlake, srcXor, nil);
- }
- }
- }
-
-
- static void GetFlakeRects(Flake* flake, Rect* rSicn, Rect* rFlake)
- {
- rSicn->left = 0;
- rSicn->right = iconSize;
- rSicn->top = flake->offset;
- rSicn->bottom = rSicn->top + iconSize;
-
- rFlake->top = flake->where.v - 7;
- rFlake->bottom = rFlake->top + iconSize;
- rFlake->left = flake->where.h - 7;
- rFlake->right = rFlake->left + iconSize;
- }
-
-
- static short BlowFlake(Flake* flake)
- {
- short delta;
- flake->where.h += delta = (1 - (randu() % 3));
- return delta;
- }
-
-
- static void HideFlakes(void)
- {
- if (! flakesHiding)
- {
- ToggleFlakes();
- flakesHiding = true;
- }
- }
-
-
- static void ToggleFlakes(void)
- {
- short count; Flake* flakep = flakes;
-
- for (count = maxFlakes; count != 0; --count, ++flakep)
- if (flakep->offset >= 0 && ! flakep->new)
- {
- // Toggle this flake.
- Rect rSicn, rFlake;
- GetFlakeRects(flakep, &rSicn, &rFlake);
- CopyBits(&icons, &snowPort.portBits, &rSicn, &rFlake, srcXor, nil);
- }
- }
-
-
- /// - support for background sound.
-
- static void PrepBgSnd(void)
- {
- if (bgWanted && LMGetSdVolume() != 0)
- {
- if (GetToolTrapAddress(_Unimplemented) != GetToolTrapAddress(_SndPlay))
- {
- // Sound Manager is available. Get sound.
- bgSndHandle = Get1IndResource('snd ',1);
- if (bgSndHandle != nil)
- {
- Ptr p;
-
- // Have the sound. Move it high and lock it down.
- MoveHHi(bgSndHandle);
- HLock(bgSndHandle);
-
- // Determine address of buffer command to start sound playing.
- // Assuming standard type 1 format sound header.
- // (Be careful if you replace the sound resource!)
- // Should be updated for SM3 call at some point.
- p = StripAddress(*bgSndHandle);
- bgSndSynth = *(short*) (p + 0x04L);
- bgSndInit = *(long*) (p + 0x06L);
- bgSndPtr = (SoundHeader*) (p + 0x14L);
- bgSndBaseRate = bgSndPtr->sampleRate;
-
- StartBgSnd();
- }
- }
- }
- }
-
-
- static void EndBgSnd(void)
- {
- if (bgSndHandle != nil)
- {
- StopBgSnd();
- ReleaseResource(bgSndHandle);
- bgSndHandle = nil;
- bgSndPtr = nil;
- }
- }
-
- #if defined(powerc)
- RoutineDescriptor upp = BUILD_ROUTINE_DESCRIPTOR(uppSndCallBackProcInfo, PlayBgSnd);
- #define soundupp &upp
- #else
- #define soundupp PlayBgSnd
- #endif
-
- static void StartBgSnd(void)
- {
- if (bgWanted)
- if (bgChannel || SndNewChannel(&bgChannel, bgSndSynth, bgSndInit, soundupp) == noErr)
- {
- bgChannel->userInfo = (long) LMGetCurrentA5();
- PlayBgSnd(bgChannel, nil);
- }
- }
-
-
- static void StopBgSnd(void)
- {
- if (bgChannel != nil)
- {
- SndDisposeChannel(bgChannel, true);
- bgChannel = nil;
- }
- }
-
-
- pascal void PlayBgSnd(SndChannelPtr chan, SndCommand* unusedcmd)
- {
- long save = SetA5(chan->userInfo); SndCommand snd;
-
- // Fiddle with the volume and pitch to give the appearance
- // of different sleigh bells. Gives it a little variety.
- snd.cmd = ampCmd;
- snd.param1 = (randu() & deltaVolume) + baseVolume;
- SndDoImmediate(chan, &snd);
-
- bgSndPtr->sampleRate = bgSndBaseRate + deltaRate - (randu() & (deltaRate*2 - 1));
-
- // Start playing sound.
- snd.cmd = bufferCmd;
- snd.param2 = (long) bgSndPtr;
- SndDoCommand(chan, &snd, true);
-
- // Queue up a callback command.
- // It will execute after the sound is finished so we can start it all again.
- snd.cmd = callBackCmd;
- SndDoCommand(chan, &snd, true);
-
- SetA5(save);
- }
-